#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <dlfcn.h>
#include <syslog.h>
#include<gio/gio.h>
#include<assert.h>
#include<fcntl.h>
#include<libusb.h>


#include "common.h"
#include "ofprint_common.h"
#include "usb_device.h"
#include "upgrade.h"
#include "ofprint-server.h"

ofp_dev *dev_ofprint = NULL;

static const struct usb_id ofp1234_id_table[] = {
    { .idVendor = 0x33a7, .idProduct = 0x2686, .description = "OMS OFP2686"},
    { 0, 0, NULL },
};

/* 定义全局变量 */
static int ofp_ops_timeout_ms = OFP_OPS_DEFAULT_TIMEOUT * 1000; /** 操作的超时时间，单位：毫秒 **/


/* 全局配置文件 */
//GKeyFile * ofplist_conf = NULL;

int ofp_onekey_powerup_identify(ofp_dev *dev);
int ofp_conf_open(GKeyFile **ofplist_conf);
void ofp_conf_free(GKeyFile **ofplist_conf);



int ofp_conf_init()
{
    GKeyFile *ofplist_conf = NULL;
    //GError *err = NULL;

    char cmd[1024] = {0};
    snprintf(cmd, 1024, "touch %s", OFP_FPRINT_LIST_FILE);
    WD_DBG("%s\n", cmd);
    if(0 > system(cmd))
        WD_ERR("exec failed!\n");

    if(0 != ofp_conf_open(&ofplist_conf))
    {
        WD_ERR("ofp_conf_open failed!!!\n");
        return -1;
    }



    ofp_conf_free(&ofplist_conf);

    return 0;
}

int ofp_conf_open(GKeyFile **ofplist_conf)
{
    *ofplist_conf = g_key_file_new();
    GError *err = NULL;

    g_key_file_load_from_file(*ofplist_conf, OFP_FPRINT_LIST_FILE, G_KEY_FILE_KEEP_TRANSLATIONS|G_KEY_FILE_KEEP_COMMENTS, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        g_key_file_free(*ofplist_conf);
        return -1;
    }

    return 0;
}

void ofp_conf_free(GKeyFile **ofplist_conf)
{
    if(NULL != *ofplist_conf)
    {
        g_key_file_free(*ofplist_conf);
        *ofplist_conf = NULL;
    }
}

#if 0
GKeyFile * ofp_bio_conf()
{
    return ofplist_conf;
}
#endif

int ofp_get_fprint_list(ofp_dev *dev)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    fprint_info *fprint_list = priv->fprint_list;
    GKeyFile * ofplist = NULL;
    GError *err = NULL;

    WD_DBG("start.\n");

    if(0 != ofp_conf_open(&ofplist))
    {
        WD_ERR("open ofplist_conf failed!!!\n");
        return -1;
    }
#if 0
    if (dev->enable == FALSE) {       // 设备不可用
        WD_ERR("device not enable!!!\n");
        ofp_conf_free(ofplist);
        return -1;
    }
#endif
    if(NULL == ofplist)
    {
        WD_ERR("ofplist_conf is NULL!!!\n");
        //ofp_conf_free(&ofplist);
        return -1;
    }

    if(0 >= dev->dev_num)
    {
        WD_ERR("device not online!!!\n");
        ofp_conf_free(&ofplist);
        return -1;
    }

    for(int i = 0; i < OFP_FPRINT_NUM_MAX; i++)
    {
        char group[OFP_GROUP_NAME_MAX] = {0};
        bool enable = FALSE;
        char *user_id = NULL;
        char *fprint_name = NULL;
        uint16 fprint_id = 0xffff;

        snprintf(group, OFP_GROUP_NAME_MAX, "UserFprint-%d", i+1);

        enable = g_key_file_get_boolean(ofplist, group, OFP_FPRINT_ENABLE, &err);
        if (err != NULL) {
            //WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
            g_error_free(err);
            err = NULL;
            continue;
        }

        fprint_list[i].enable = enable;

        if(TRUE != enable)
        {
            continue;
        }
        //WD_DBG("enable = %d.\n", enable);

        user_id = g_key_file_get_string(ofplist, group, OFP_FPRINT_USER_ID, &err);
        if (err != NULL) {
            WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
            g_error_free(err);
            err = NULL;
            continue;
        }
        strcpy(fprint_list[i].user_id, user_id);
        WD_DBG("user_id = %s.\n", fprint_list[i].user_id);


        fprint_name = g_key_file_get_string(ofplist, group, OFP_FPRINT_NAME, &err);
        if (err != NULL) {
            WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
            g_error_free(err);
            err = NULL;
            continue;
        }
        strcpy(fprint_list[i].fprint_name, fprint_name);
        WD_DBG("fprint_name = %s.\n", fprint_list[i].fprint_name);

        fprint_id = g_key_file_get_integer(ofplist, group, OFP_FPRINT_ID, &err);
        if (err != NULL) {
            WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
            g_error_free(err);
            err = NULL;
            continue;
        }
        fprint_list[i].fprint_id = fprint_id;
        WD_DBG("fprint_id = %d.\n", fprint_list[i].fprint_id);

    }

    ofp_conf_free(&ofplist);
    WD_DBG("end.\n");

    return 0;
}

int ofp_find_fprint_info(ofp_dev *dev, char *user_id, char *fprint_name, fprint_info **fprint_found)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    fprint_info *fprint_list = priv->fprint_list;
    int index = 0;

    if((NULL == dev)||(NULL == user_id)||(NULL == fprint_name)||(NULL == fprint_found))
    {
        WD_ERR(_("the param is error!!!\n"));
        return index;
    }

    WD_DBG("start.\n");

    for(int i = 0; i < OFP_FPRINT_NUM_MAX; i++)
    {
        if((TRUE == fprint_list[i].enable)\
            && (0 == strcmp(user_id, fprint_list[i].user_id))\
            && (0 == strcmp(fprint_name, fprint_list[i].fprint_name)))
        {
            *fprint_found = &(fprint_list[i]);
            index = i+1;
            break;
        }
    }

    WD_DBG("index = %d.\n", index);

    return index;
}

int ofp_find_fprint_info_byfprintid(ofp_dev *dev, uint16 fprintid, fprint_info **fprint_found)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    fprint_info *fprint_list = priv->fprint_list;
    int index = 0;

    WD_DBG("start.\n");

    for(int i = 0; i < OFP_FPRINT_NUM_MAX; i++)
    {
        if((TRUE == fprint_list[i].enable)\
            && (fprintid == fprint_list[i].fprint_id))
        {
            *fprint_found = &(fprint_list[i]);
            index = i+1;
            break;
        }
    }

    WD_DBG("index = %d.\n", index);

    return index;
}


int ofp_get_empty_fprint_info(ofp_dev *dev)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    fprint_info *fprint_list = priv->fprint_list;
    int index = 0;

    for(int i = 0; i < OFP_FPRINT_NUM_MAX; i++)
    {
        if(FALSE == fprint_list[i].enable)
        {
            index = i + 1;
            break;
        }
    }

    return index;
}

int ofp_set_fprint_info(ofp_dev *dev, int index, fprint_info *xfprint_info)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    fprint_info *fprint_list = priv->fprint_list;
    GKeyFile * ofplist = NULL;
    GError *err = NULL;
    char group[OFP_GROUP_NAME_MAX] = {0};

    if((NULL == dev) || (NULL == xfprint_info)\
        ||(0 >= index) || (OFP_FPRINT_NUM_MAX < index))
    {
        WD_ERR("the param is error!!!\n");
        return -1;
    }

    if(0 != ofp_conf_open(&ofplist))
    {
        WD_ERR("open ofplist_conf failed!!!\n");
        return -1;
    }

    snprintf(group, OFP_GROUP_NAME_MAX, "UserFprint-%d", index);

    WD_DBG("[%s] enable=%d user_id=%s fprint_name=%s fprint_id=%d\n", \
        group, xfprint_info->enable, xfprint_info->user_id, xfprint_info->fprint_name, xfprint_info->fprint_id);

    WD_DBG("\n");
    gsize length;
    g_key_file_to_data(ofplist, &length, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        ofp_conf_free(&ofplist);
        return -1;
    }

    WD_DBG("\n");
    g_key_file_set_boolean(ofplist, group, OFP_FPRINT_ENABLE, xfprint_info->enable);
    g_key_file_set_string(ofplist, group, OFP_FPRINT_USER_ID, xfprint_info->user_id);
    g_key_file_set_string(ofplist, group, OFP_FPRINT_NAME, xfprint_info->fprint_name);
    g_key_file_set_integer(ofplist, group, OFP_FPRINT_ID, xfprint_info->fprint_id);

    WD_DBG("\n");
    g_key_file_save_to_file(ofplist, OFP_FPRINT_LIST_FILE, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        ofp_conf_free(&ofplist);
        return -1;
    }

    WD_DBG("\n");
    fprint_list[index-1].enable = xfprint_info->enable;
    strcpy(fprint_list[index-1].user_id, xfprint_info->user_id);
    strcpy(fprint_list[index-1].fprint_name, xfprint_info->fprint_name);
    fprint_list[index-1].fprint_id = xfprint_info->fprint_id;
    WD_DBG("\n");

    ofp_conf_free(&ofplist);

    return 0;
}


int ofp_add_fprint_info(ofp_dev *dev, fprint_info *xfprint_info)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    fprint_info *fprint_list = priv->fprint_list;
    fprint_info *fprint_found = NULL;
    int index = 0;

    WD_DBG("start.\n");

    ofp_find_fprint_info(dev, xfprint_info->user_id, xfprint_info->fprint_name, &fprint_found);

    if(NULL != fprint_found)
    {
        WD_ERR("fprint_info user_id=%s, fprint_name=%s already existed!!!\n", \
            xfprint_info->user_id, xfprint_info->fprint_name);
        return -1;
    }

    index = ofp_get_empty_fprint_info(dev);

    if(0 >= index)
    {
        WD_ERR("ofp_get_empty_fprint_info failed!!!\n");
        return -1;
    }

    if(0 != ofp_set_fprint_info(dev, index, xfprint_info))
    {
        WD_ERR("ofp_set_fprint_info failed!!!\n");
        return -1;
    }

    WD_DBG("end.\n");

    return 0;
}

int ofp_del_fprint_info(ofp_dev *dev, char *user_id, char *fprint_name)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    fprint_info *fprint_list = priv->fprint_list;
    fprint_info *fprint_found = NULL;
    int index = 0;

    index = ofp_find_fprint_info(dev, user_id, fprint_name, &fprint_found);

    if(NULL == fprint_found)
    {
        return 0;
    }

    if(TRUE == fprint_found->enable)
    {
        fprint_found->enable = FALSE;
        ofp_set_fprint_info(dev, index, fprint_found);
    }

    return 0;
}


int32 driver_config_init(DRIVER_CONFIG_STRUCT *config)
{
    GKeyFile *file = NULL;
    GError *err = NULL;
    int auto_suspend = 0;
    char *str_upgrade_mode = NULL;
    int printf_log_flag = 0;
    int upgrade_flag = 0;

    WD_DBG("start.\n");

    file = g_key_file_new();

    g_key_file_load_from_file(file, OFP_DRIVER_CONFIG_FILE, G_KEY_FILE_KEEP_TRANSLATIONS|G_KEY_FILE_KEEP_COMMENTS, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        g_key_file_free(file);
        return -1;
    }

    printf_log_flag = g_key_file_get_integer(file, BASIC_PARAM, PRINTF_LOG_FLAG, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        printf_log_flag = 0;
    }
    if(0 != printf_log_flag)
    {
        set_printf_log_grade(printf_log_flag);
        WD_LOG("================enable printf log================\n");
        WD_LOG("%s = %d.\n", PRINTF_LOG_FLAG, printf_log_flag);
    }

    auto_suspend = g_key_file_get_integer(file, BASIC_PARAM, AUTO_SUSPEND, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        auto_suspend = 0;
    }
    WD_DBG("set auto_suspend: %d\n", auto_suspend);
    config->autosuspend_flag = auto_suspend;

    upgrade_flag = g_key_file_get_integer(file, BASIC_PARAM, STR_UPGRADE_FLAG, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        upgrade_flag = 0;
    }
    WD_DBG("set upgrade_flag: %d\n", upgrade_flag);
    config->upgrade_flag = upgrade_flag;

    str_upgrade_mode = g_key_file_get_string(file, BASIC_PARAM, UPGRADE_MODE, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        str_upgrade_mode = NULL;
    }

    if(NULL == str_upgrade_mode)
    {
        WD_DBG("set upgrade mode: %s\n", STR_AUTO_UPGRADE);
        config->upgrade_mode = AUTO_UPGRADE;
    }
    else if(0 == strcmp(STR_FORCE_UPGRADE, str_upgrade_mode))
    {
        WD_DBG("set upgrade mode: %s\n", STR_FORCE_UPGRADE);
        config->upgrade_mode = FORCE_UPGRADE;
    }
    else if(0 == strcmp(STR_DONT_UPGRADE, str_upgrade_mode))
    {
        WD_DBG("set upgrade mode: %s\n", STR_DONT_UPGRADE);
        config->upgrade_mode = DONT_UPGRADE;
    }
    else if(0 == strcmp(STR_AUTO_UPGRADE, str_upgrade_mode))
    {
        WD_DBG("set upgrade mode: %s\n", STR_AUTO_UPGRADE);
        config->upgrade_mode = AUTO_UPGRADE;
    }
    else if(0 == strcmp(STR_SMART_UPGRADE, str_upgrade_mode))
    {
        WD_DBG("set upgrade mode: %s\n", STR_SMART_UPGRADE);
        config->upgrade_mode = SMART_UPGRADE;
    }

    if(NULL != str_upgrade_mode)
    {
        g_free(str_upgrade_mode);
    }

    g_key_file_free(file);
    WD_DBG("end.\n");

    return 0;
}

int read_fw_upgrade_flag(void)
{
    GKeyFile *file = NULL;
    GError *err = NULL;
    int upgrade_flag = 0;
    int ret = -1;

    WD_DBG("start.\n");

    file = g_key_file_new();

    g_key_file_load_from_file(file, OFP_DRIVER_CONFIG_FILE, G_KEY_FILE_KEEP_TRANSLATIONS|G_KEY_FILE_KEEP_COMMENTS, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        g_key_file_free(file);
        return -1;
    }

    upgrade_flag = g_key_file_get_integer(file, BASIC_PARAM, STR_UPGRADE_FLAG, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        upgrade_flag = 0;
    }
    WD_DBG("upgrade_flag = %d\n", upgrade_flag);

    if(0 >= upgrade_flag)
    {
        ret = -1;
    }
    else
    {
        ret = 0;
    }

end:
    g_key_file_free(file);
    WD_DBG("end.\n");
    return ret;
}

int change_fw_upgrade_flag(void)
{
    GKeyFile *file = NULL;
    GError *err = NULL;
    int upgrade_flag = 0;
    int ret = -1;
    static uint8 upgrade_times = 0;

    WD_DBG("start.\n");

    if(0 < upgrade_times)
    {
        WD_DBG("upgrade_flag has been changed.\n");
        return 0;
    }

    file = g_key_file_new();

    g_key_file_load_from_file(file, OFP_DRIVER_CONFIG_FILE, G_KEY_FILE_KEEP_TRANSLATIONS|G_KEY_FILE_KEEP_COMMENTS, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        g_key_file_free(file);
        return -1;
    }

    upgrade_flag = g_key_file_get_integer(file, BASIC_PARAM, STR_UPGRADE_FLAG, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        upgrade_flag = 0;
        goto end;
    }
    WD_DBG("upgrade_flag = %d\n", upgrade_flag);
    if(0 >= upgrade_flag)
    {
        ret = 0;
        goto end;
    }

    upgrade_flag--;

    gsize length;
    g_key_file_to_data(file, &length, &err);
    if (err != NULL) {
        WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        goto end;
    }
    else
    {
        g_key_file_set_integer(file, BASIC_PARAM, STR_UPGRADE_FLAG, upgrade_flag);
        g_key_file_save_to_file(file, OFP_DRIVER_CONFIG_FILE, &err);
        if (err != NULL) {
            WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
            g_error_free(err);
            err = NULL;
            goto end;
        }
    }

    ret = 0;
    upgrade_times++;
    WD_DBG("change upgrade_flag = %d\n", upgrade_flag);

end:
    g_key_file_free(file);
    WD_DBG("end.\n");
    return ret;
}


#if 0
int ofp_dev_is_enable(bio_dev * dev, GKeyFile * conf)
{

    GError * err = NULL;
    if (dev->device_name == NULL)
        return FALSE;

    gboolean enable = g_key_file_get_boolean(conf, dev->device_name,
                                               "Enable", &err);
    if (err != NULL) {
        bio_print_error(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
    } else bio_print_debug("IsEnable = %s\n", (enable)?"TRUE":"FALSE");
    return enable;
}
#endif

int bio_init(void) {
    int ret = 0;
#if 0
    setlocale (LC_ALL, "");
    bindtextdomain(LIBBIOMETRIC_DOMAIN_NAME, LOCALEDIR);

    bio_print_debug(_("Biometric framework API version:\n"));
    bio_print_debug(_("         Driver API(DRV_API): %d.%d.%d\n"),
                    BIO_DRV_API_VERSION_MAJOR,
                    BIO_DRV_API_VERSION_MINOR,
                    BIO_DRV_API_VERSION_FUNC);
    bio_print_debug(_("    Application API(APP_API): %d.%d.%d\n"),
                    BIO_APP_API_VERSION_MAJOR,
                    BIO_APP_API_VERSION_MINOR,
                    BIO_APP_API_VERSION_FUNC);

    /* 初始化配置 */
    if (bio_conf_init() != 0) {
        bio_print_error(_("bio_conf_init failed\n"));
        return -1;
    }

    /* 检测并升级数据库 */
    sqlite3 *db = bio_sto_connect_db();
    ret = bio_sto_check_and_upgrade_db_format(db);
    if (ret != 0 )
    {
        bio_sto_disconnect_db(db);
        return -1;
    }
#endif
    int rc;
    //rc = libusb_init (NULL);
    if (rc < 0) {
        //bio_print_error(_("failed to initialise libusb: %s\n"), libusb_error_name(rc));
        return -1;
    }

    return 0;
}

int bio_close(void) {
    //libusb_exit (NULL);

    //bio_conf_free();

    return 0;
}

int ofp_ops_stop_ops(ofp_dev *dev, int waiting_sec){
    //return dev->ofp_stop_by_user(dev, waiting_sec * 1000);
    return 0;
}

void ofp_set_ops_timeout_ms(int t) {
    ofp_ops_timeout_ms = t;
}

int ofp_get_ops_timeout_ms() {
    return ofp_ops_timeout_ms;
}

bool ofp_is_stop_by_user(ofp_dev * dev) {
    if (dev->dev_status % 100 == DEVS_COMM_STOP_BY_USER)
        return TRUE;
    else
        return FALSE;
}

int ofp_get_dev_status(ofp_dev * dev) {
    return dev->dev_status;
}

void ofp_set_dev_status(ofp_dev * dev, int status) {
    dev->dev_status = status;

    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_DEVICE);
}

void ofp_set_dev_abs_status(ofp_dev * dev, int status) {
    dev->dev_status = status;

    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_DEVICE);
}

int ofp_get_ops_result(ofp_dev * dev) {
    return dev->ops_result;
}

void ofp_set_ops_result(ofp_dev * dev, int result) {
    int ops = 0;
    ops = ofp_get_dev_status(dev) / 100;
    result  = result % 100;
    dev->ops_result = ops * 100 + result;

    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_OPERATION);
}

void ofp_set_ops_abs_result(ofp_dev * dev, int result) {
    dev->ops_result = result;

    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_OPERATION);
}

int ofp_get_notify_mid(ofp_dev * dev) {
    return dev->notify_mid;
}

void ofp_set_notify_mid(ofp_dev * dev, int mid) {
    int ops = 0;
    ops = ofp_get_dev_status(dev) / 100;
    mid  = mid % 100;
    dev->notify_mid = ops * 100 + mid;

    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_NOTIFY);
}

void ofp_set_notify_abs_mid(ofp_dev * dev, int mid) {
    dev->notify_mid = mid;

    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_NOTIFY);
}

void ofp_set_all_status(ofp_dev * dev,
                            int dev_status,
                            int ops_result,
                            int notify_mid)
{
    int type = ofp_get_dev_status(dev) / 100;
    dev->dev_status = type * 100 + (dev_status % 100);
    dev->ops_result = type * 100 + (ops_result % 100);
    dev->notify_mid = type * 100 + (notify_mid % 100);

    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_DEVICE);
    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_OPERATION);
    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_NOTIFY);
}

void ofp_set_all_abs_status(ofp_dev * dev,
                            int dev_status,
                            int ops_result,
                            int notify_mid)
{
    dev->dev_status = dev_status;
    dev->ops_result = ops_result;
    dev->notify_mid = notify_mid;

    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_DEVICE);
    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_OPERATION);
    ofp_status_changed_common_callback(dev->driver_id, STATUS_TYPE_NOTIFY);
}

const char * ofp_get_dev_status_mesg(ofp_dev *dev){
    const char * mesg = NULL;

    //mesg = dev->ofp_get_dev_status_mesg(dev);
    if (mesg != NULL)
        return mesg;
    switch (ofp_get_dev_status(dev)) {
    case DEVS_COMM_IDLE:
        return _("Device idle");
    case DEVS_COMM_DOING:
        return _("Device is in process");
    case DEVS_COMM_STOP_BY_USER:
        return _("Terminating current operation");
    case DEVS_COMM_DISABLE:
        return _("Device is disabled");

    case DEVS_OPEN_DOING:
        return _("Opening device");
    case DEVS_OPEN_STOP_BY_USER:
        return _("Terminating open operation");

    case DEVS_ENROLL_DOING:
        return _("Enrolling");
    case DEVS_ENROLL_STOP_BY_USER:
        return _("Terminating enroll operation");

    case DEVS_VERIFY_DOING:
        return _("Verifying");
    case DEVS_VERIFY_STOP_BY_USER:
        return _("Terminating Verify operation");

    case DEVS_IDENTIFY_DOING:
        return _("Identifying");
    case DEVS_IDENTIFY_STOP_BY_USER:
        return _("Terminating identify operation");

    case DEVS_CAPTURE_DOING:
        return _("Capturing");
    case DEVS_CAPTURE_STOP_BY_USER:
        return _("Terminating capture operation");

    case DEVS_SEARCH_DOING:
        return _("Searching");
    case DEVS_SEARCH_STOP_BY_USER:
        return _("Terminating search operation");

    case DEVS_CLEAN_DOING:
        return _("Cleaning");
    case DEVS_CLEAN_STOP_BY_USER:
        return _("Terminating clean operation");

    case DEVS_GET_FLIST_DOING:
        return _("Getting feature list");
    case DEVS_GET_FLIST_STOP_BY_USER:
        return _("Terminating get feature list operation");

    case DEVS_RENAME_DOING:
        return _("Renaming feature");
    case DEVS_RENAME_STOP_BY_USER:
        return _("Terminating feature rename operation");

    case DEVS_CLOSE_DOING:
        return _("Closing");
    case DEVS_CLOSE_STOP_BY_USER:
        return _("Terminating close operation");

    default:
        //ofp_print_warning(_("Device unknown status code: %d\n"), ofp_get_dev_status(dev));
        return _("Device is in an unknown status...");
    }
}

const char * ofp_get_ops_result_mesg(ofp_dev *dev){
    const char * mesg = NULL;
    //mesg = dev->ops_get_ops_result_mesg(dev);
    if (mesg != NULL)
        return mesg;

    switch (ofp_get_ops_result(dev)) {
    case OPS_COMM_SUCCESS:
        return _("Operation successful");
    case OPS_COMM_FAIL:
        return _("Operation failed");
    case OPS_COMM_ERROR:
        return _("An error was encountered during the operation");
    case OPS_COMM_STOP_BY_USER:
        return _("The operation was interrupted by the user");
    case OPS_COMM_TIMEOUT:
        return _("Operation timeout");
    case OPS_COMM_OUT_OF_MEM:
        return _("Out of memory");

    case OPS_OPEN_SUCCESS:
        return _("Open device completion");
    case OPS_OPEN_FAIL:
        return _("Open device failed");
    case OPS_OPEN_ERROR:
        return _("An error was encountered during the opening");

    case OPS_ENROLL_SUCCESS:
        return _("Enroll feature successful");
    case OPS_ENROLL_FAIL:
        return _("Enroll feature failed");
    case OPS_ENROLL_ERROR:
        return _("An error was encountered during enrolling");
    case OPS_ENROLL_STOP_BY_USER:
        return _("Enroll operation was interrupted by the user");
    case OPS_ENROLL_TIMEOUT:
        return _("Enroll operation timeout");

    case OPS_VERIFY_MATCH:
        return _("Verify feature match");
    case OPS_VERIFY_NO_MATCH:
        return _("Verify feature no match");
    case OPS_VERIFY_ERROR:
        return _("An error was encountered during verifying");
    case OPS_VERIFY_STOP_BY_USER:
        return _("Verify operation was interrupted by the user");
    case OPS_VERIFY_TIMEOUT:
        return _("Verify operation timeout");

    case OPS_IDENTIFY_MATCH:
        return _("Identify feature match");
    case OPS_IDENTIFY_NO_MATCH:
        return _("Identify feature no match");
    case OPS_IDENTIFY_ERROR:
        return _("An error was encountered during identifying");
    case OPS_IDENTIFY_STOP_BY_USER:
        return _("Identify operation was interrupted by the user");
    case OPS_IDENTIFY_TIMEOUT:
        return _("Identify operation timeout");

    case OPS_CAPTURE_SUCCESS:
        return _("Capture feature successful");
    case OPS_CAPTURE_FAIL:
        return _("Capture feature failed");
    case OPS_CAPTURE_ERROR:
        return _("An error was encountered during capturing");
    case OPS_CAPTURE_STOP_BY_USER:
        return _("Capture operation was interrupted by the user");
    case OPS_CAPTURE_TIMEOUT:
        return _("Capture operation timeout");

    case OPS_SEARCH_MATCH:
        return _("Find the specified features");
    case OPS_SEARCH_NO_MATCH:
        return _("No specified features were found");
    case OPS_SEARCH_ERROR:
        return _("An error was encountered during searching");
    case OPS_SEARCH_STOP_BY_USER:
        return _("Search operation was interrupted by the user");
    case OPS_SEARCH_TIMEOUT:
        return _("Search operation timeout");

    case OPS_CLEAN_SUCCESS:
        return _("Clean feature successful");
    case OPS_CLEAN_FAIL:
        return _("Clean feature failed");
    case OPS_CLEAN_ERROR:
        return _("An error was encountered during cleaning");
    case OPS_CLEAN_STOP_BY_USER:
        return _("Clean operation was interrupted by the user");
    case OPS_CLEAN_TIMEOUT:
        return _("Clean operation timeout");

    case OPS_GET_FLIST_SUCCESS:
        return _("Get feature list completion");
    case OPS_GET_FLIST_FAIL:
        return _("Get feature list failed");
    case OPS_GET_FLIST_ERROR:
        return _("An error was encountered during getting feature list");

    case OPS_RENAME_SUCCESS:
        return _("Rename feature successful");
    case OPS_RENAME_FAIL:
        return _("Rename feature failed");
    case OPS_RENAME_ERROR:
        return _("An error was encountered during renaming");

    case OPS_CLOSE_SUCCESS:
        return _("Close device completion");
    case OPS_CLOSE_FAIL:
        return _("Close device failed");
    case OPS_CLOSE_ERROR:
        return _("An error was encountered during closing");

    default:
        return _("Operation is in an unknown status......");
    }
}

const char * ofp_get_notify_mid_mesg(ofp_dev *dev){
    const char * mesg = NULL;
    //mesg = dev->ops_get_notify_mid_mesg(dev);
    if (mesg != NULL)
        return mesg;

    switch (ofp_get_notify_mid(dev)) {
    case NOTIFY_COMM_IDLE:
        return _("Device idle");
    case NOTIFY_COMM_FAIL:
        return _("Operation failed");
    case NOTIFY_COMM_ERROR:
        return _("An error was encountered during the operation");
    case NOTIFY_COMM_STOP_BY_USER:
        return _("The operation was interrupted by the user");
    case NOTIFY_COMM_TIMEOUT:
        return _("Operation timeout");
    case NOTIFY_COMM_DISABLE:
        return _("Device is disable");
    case NOTIFY_COMM_OUT_OF_MEM:
        return _("Out of memory");
    case NOTIFY_COMM_UNSUPPORTED_OPS:
        return _("The device or driver does not support this operation");

    case NOTIFY_OPEN_SUCCESS:
        return _("Open device completion");
    case NOTIFY_OPEN_FAIL:
        return _("Open device failed");
    case NOTIFY_OPEN_ERROR:
        return _("An error was encountered during the opening");

    case NOTIFY_ENROLL_SUCCESS:
        return _("Enroll feature successful");
    case NOTIFY_ENROLL_FAIL:
        return _("Enroll feature failed");
    case NOTIFY_ENROLL_ERROR:
        return _("An error was encountered during enrolling");
    case NOTIFY_ENROLL_STOP_BY_USER:
        return _("Enroll operation was interrupted by the user");
    case NOTIFY_ENROLL_TIMEOUT:
        return _("Enroll operation timeout");

    case NOTIFY_VERIFY_MATCH:
        return _("Verify feature match");
    case NOTIFY_VERIFY_NO_MATCH:
        return _("Verify feature no match");
    case NOTIFY_VERIFY_ERROR:
        return _("An error was encountered during verifying");
    case NOTIFY_VERIFY_STOP_BY_USER:
        return _("Verify operation was interrupted by the user");
    case NOTIFY_VERIFY_TIMEOUT:
        return _("Verify operation timeout");

    case NOTIFY_IDENTIFY_MATCH:
        return _("Identify feature match");
    case NOTIFY_IDENTIFY_NO_MATCH:
        return _("Identify feature no match");
    case NOTIFY_IDENTIFY_ERROR:
        return _("An error was encountered during identifying");
    case NOTIFY_IDENTIFY_STOP_BY_USER:
        return _("Identify operation was interrupted by the user");
    case NOTIFY_IDENTIFY_TIMEOUT:
        return _("Identify operation timeout");

    case NOTIFY_CAPTURE_SUCCESS:
        return _("Capture feature successful");
    case NOTIFY_CAPTURE_FAIL:
        return _("Capture feature failed");
    case NOTIFY_CAPTURE_ERROR:
        return _("An error was encountered during capturing");
    case NOTIFY_CAPTURE_STOP_BY_USER:
        return _("Capture operation was interrupted by the user");
    case NOTIFY_CAPTURE_TIMEOUT:
        return _("Capture operation timeout");

    case NOTIFY_SEARCH_MATCH:
        return _("Find the specified features");
    case NOTIFY_SEARCH_NO_MATCH:
        return _("No specified features were found");
    case NOTIFY_SEARCH_ERROR:
        return _("An error was encountered during searching");
    case NOTIFY_SEARCH_STOP_BY_USER:
        return _("Search operation was interrupted by the user");
    case NOTIFY_SEARCH_TIMEOUT:
        return _("Search operation timeout");

    case NOTIFY_CLEAN_SUCCESS:
        return _("Clean feature successful");
    case NOTIFY_CLEAN_FAIL:
        return _("Clean feature failed");
    case NOTIFY_CLEAN_ERROR:
        return _("An error was encountered during cleaning");
    case NOTIFY_CLEAN_STOP_BY_USER:
        return _("Clean operation was interrupted by the user");
    case NOTIFY_CLEAN_TIMEOUT:
        return _("Clean operation timeout");

    case NOTIFY_GET_FLIST_SUCCESS:
        return _("Get feature list completion");
    case NOTIFY_GET_FLIST_FAIL:
        return _("Get feature list failed");
    case NOTIFY_GET_FLIST_ERROR:
        return _("An error was encountered during getting feature list");

    case NOTIFY_RENAME_SUCCESS:
        return _("Rename feature successful");
    case NOTIFY_RENAME_FAIL:
        return _("Rename feature failed");
    case NOTIFY_RENAME_ERROR:
        return _("An error was encountered during renaming");
    case NOTIFY_RENAME_INCOMPLETE:
        return _("Feature renaming is not complete, please try again");

    case NOTIFY_CLOSE_SUCCESS:
        return _("Close device completion");
    case NOTIFY_CLOSE_FAIL:
        return _("Close device failed");
    case NOTIFY_CLOSE_ERROR:
        return _("An error was encountered during closing");

    default:
        return _("Device internal error");
    }
}

/*
 * 检测usb设备，返回检测到的数量
 */
int ofp_common_detect_usb_device(ofp_dev *dev)
{
    int num = 0;

    return num;
}

/********** 内部函数实现 **********/
/* 分配一个ofp_dev资源 */
ofp_dev * ofp_dev_new()
{
    ofp_dev * dev;
    dev = malloc(sizeof(ofp_dev));
    if (dev != NULL)
        memset(dev, 0, sizeof(ofp_dev));
    return dev;
}

/* 释放bio_dev资源 */
void ofp_dev_free(ofp_dev * dev)
{
    dev->ops_free(dev);

    free(dev);
}

/* 状态变更的回调函数 */
void ofp_status_changed_common_callback(int drvid, int type)
{

}

/* 给上层服务提供的USB热插拔事件的回调函数接口 */
void ofp_usb_device_hot_plug_common_callbak(int drvid, int action, int dev_num_now)
{

}

void ofp_ops_free(ofp_dev *dev)
{
    if(NULL != dev->dev_priv)
    {
        g_free(dev->dev_priv);
    }
}

int ofp_ops_discover(ofp_dev *dev)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    FP_DEVICE_PARAM_STRUCT *dev_param = &(priv->fp_device_param);
    USB_DEV_STRUCT *usb_dev = &(dev_param->usb_dev);
    INTF_USB_ID_STRUCT *id_table = (INTF_USB_ID_STRUCT *)(dev->usb_info.id_table);
    INTF_USB_ID_STRUCT *usb_id;

    for (uint8 count = 0; id_table[count].idVendor != 0; count++)
    {
        usb_id = (INTF_USB_ID_STRUCT *)&id_table[count];
        WD_DBG("usb id %04x:%04x ", usb_id->idVendor, usb_id->idProduct);
        if(0 == intf_usb_check_device_exist(usb_id))
        {
            W_DBG("exist.\n");
            usb_dev->usb_id = *usb_id;
            if(0 == intf_usb_get_device_path(&(usb_dev->usb_id), usb_dev->dev_port_path, DEV_PORT_PATH_LEN))
                usb_dev_enable_autosuspend(usb_dev);
            dev->dev_num = 1;
            return dev->dev_num;
        }
        W_DBG("not exist!\n");
    }

    dev->dev_num = 0;
    return dev->dev_num;
}

/* USB设备插入时的回调函数 */
static int ofp_usb_hotplug_callback_attach(libusb_context *ctx,
                                           libusb_device *usbdev,
                                           libusb_hotplug_event event,
                                           void *user_data)
{
    struct libusb_device_descriptor desc;
    int rc;

    WD_DBG("start *************\r\n");

    rc = libusb_get_device_descriptor(usbdev, &desc);
    if (LIBUSB_SUCCESS != rc) {
        WD_ERR(_("Error: Can not get device descriptor\n"));
    }

    WD_LOG(_("Device attached: %04x:%04x\n"), desc.idVendor, desc.idProduct);

    ofp_dev * dev = dev_ofprint;
    driver_info *priv = (driver_info *)dev->dev_priv;
    FP_DEVICE_PARAM_STRUCT *dev_param = &(priv->fp_device_param);
    USB_DEV_STRUCT *usb_dev = &(dev_param->usb_dev);
    INTF_USB_ID_STRUCT *id_table = (INTF_USB_ID_STRUCT *)(dev->usb_info.id_table);

    if (dev->enable == 0)
    {
        return 0;
    }
    dev->ops_discover(dev);

    if(dev->dev_num > 0)
    {
        priv->dev_state |= DeviceStateNormal;
        priv->set_dev_state(priv->dev_state);
    }

    if(DEVS_COMM_IDLE != ofp_get_dev_status(dev))
    {
        usb_dev_open(usb_dev, id_table);//当识别到错误指纹时，usb会断开，此处规避，add by hcq, 20230323
    }

    WD_DBG("end *************\r\n");

    return 0;
}

/* USB设备拔出时的回调函数 */
static int ofp_usb_hotplug_callback_detach(libusb_context *ctx,
                                           libusb_device *usbdev,
                                           libusb_hotplug_event event,
                                           void *user_data)
{
    struct libusb_device_descriptor desc;
    int rc;

    WD_DBG("start *************\r\n");

    rc = libusb_get_device_descriptor(usbdev, &desc);
    if (LIBUSB_SUCCESS != rc) {
        WD_DBG(_("Error getting device descriptor"));
    }

    WD_LOG(_("Device detached: %04x:%04x\n"), desc.idVendor, desc.idProduct);

    ofp_dev * dev = dev_ofprint;
    driver_info *priv = (driver_info *)dev->dev_priv;
    FP_DEVICE_PARAM_STRUCT *dev_param = &(priv->fp_device_param);
    USB_DEV_STRUCT *usb_dev = &(dev_param->usb_dev);

    if (dev->enable == 0)
    {
        return 0;
    }
    dev->ops_discover(dev);

    if (dev->dev_num <= 0)
    {
        priv->dev_state &=~ DeviceStateNormal;
        //priv->set_dev_state(priv->dev_state);//设备拔出时不再设置状态为设备不可用, by hcq, 20231102
    }

    usb_dev_close(usb_dev);
    WD_DBG("end *************\r\n");

    return 0;
}

void monitor_usb_hotplug(void* para)
{
    int rc =0;

    while (1)
    {
        rc = libusb_handle_events(dev_ofprint->ctx);
        if (rc < 0)
            WD_ERR(_("libusb_handle_events failed: %s\n"), libusb_error_name(rc));
    }
}


int ofp_drv_init(ofp_dev *dev)
{
    GKeyFile * conf = NULL;
    gchar **  drv_list_conf = NULL;
    gsize driver_count = 0;
    ofp_dev * p = dev;

    /* 检测驱动版本*/
    WD_LOG(_("%s driver DRV_API version: %d.%d.%d\n"),
                    p->device_name,
                    p->drv_api_version.major,
                    p->drv_api_version.minor,
                    p->drv_api_version.function);

    /* 注册USB热插拔时的回掉函数 */
    int rc = 0;
    int i;
    const struct usb_id * id_table = p->usb_info.id_table;
    if(!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
    {
        WD_LOG("this libusb doesn't support hotplug.")
        dev->ops_discover(dev);
        return 0;
    }

    for (i = 0; id_table[i].idVendor != 0; i++)
    {
        WD_DBG("i = %d, idVendor = 0x%x, idProduct = 0x%x\n", \
            i, id_table[i].idVendor, id_table[i].idProduct);
        rc = libusb_hotplug_register_callback(dev->ctx,
                                              LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
                                              0,
                                              id_table[i].idVendor,
                                              id_table[i].idProduct,
                                              LIBUSB_HOTPLUG_MATCH_ANY,
                                              ofp_usb_hotplug_callback_attach,
                                              NULL,
                                              &(p->usb_info.callback_handle)[0]);
        if (LIBUSB_SUCCESS != rc) {
            WD_ERR(_("Error: Can not register attach callback error\n"));
        }

        rc = libusb_hotplug_register_callback (dev->ctx,
                                               LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
                                               0,
                                               id_table[i].idVendor,
                                               id_table[i].idProduct,
                                               LIBUSB_HOTPLUG_MATCH_ANY,
                                               ofp_usb_hotplug_callback_detach,
                                               NULL,
                                               &(p->usb_info.callback_handle)[1]);
        if (LIBUSB_SUCCESS != rc) {
            WD_ERR(_("Error: Can not register detach callback error\n"));
        }
    }

    int err = 0;
    pthread_t thr;

    err = pthread_create(&thr, NULL, (void *)monitor_usb_hotplug, NULL);
    if (err != 0) {
        WD_ERR(_("Can't create thread: %s\n"), strerror(err));
    }

    WD_DBG(_("end\n"));

    return 0;
}

int ofp_param_config(ofp_dev *dev)
{
    driver_info *priv = (driver_info *)dev->dev_priv;
    int mode_flag = 0;
    int reboot_flag = 0;
    bool enable = TRUE;
    GKeyFile *ofplist = NULL;
    GError *err = NULL;
    int update_enable = 0;
    int printf_log_flag = 0;

    WD_DBG("start.\n");

    if(0 != ofp_conf_open(&ofplist))
    {
        WD_ERR("open ofplist_conf failed!!!\n");
        return -1;
    }

    enable = g_key_file_get_boolean(ofplist, OFP_DEVICE_PARAM, OFP_DEVICE_ENABLE, &err);
    if (err != NULL)
    {
        g_error_free(err);
        err = NULL;
        enable = TRUE;
        update_enable = 1;
    }
    dev->enable = enable;
    WD_DBG("device enable = %d.\n", enable);

    printf_log_flag = g_key_file_get_integer(ofplist, OFP_DEVICE_PARAM, PRINTF_LOG_FLAG, &err);
    if (err != NULL) {
        //WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        printf_log_flag = 0;
    }
    if(0 != printf_log_flag)
    {
        set_printf_log_grade(printf_log_flag);
        WD_LOG("%s = %d.\n", PRINTF_LOG_FLAG, printf_log_flag);
    }


    mode_flag = g_key_file_get_integer(ofplist, OFP_DEVICE_PARAM, OFP_MODE_FLAG, &err);
    if (err != NULL) {
        //WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        mode_flag = 0;
    }
    priv->mode_flag = mode_flag;
    WD_LOG("%s = %d.\n", OFP_MODE_FLAG, priv->mode_flag);

    reboot_flag = g_key_file_get_integer(ofplist, OFP_DEVICE_PARAM, OFP_REBOOT_FLAG, &err);
    if (err != NULL) {
        //WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
        g_error_free(err);
        err = NULL;
        reboot_flag = 0;
    }
    priv->reboot_flag = reboot_flag;
    WD_LOG("%s = %d.\n", OFP_REBOOT_FLAG, priv->reboot_flag);

    if((0 != update_enable)||(0 != mode_flag)||(0 != reboot_flag))
    {
        gsize length;
        g_key_file_to_data(ofplist, &length, &err);
        if (err != NULL) {
            WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
            g_error_free(err);
            err = NULL;
        }
        else
        {
            if(0 != update_enable)
                g_key_file_set_boolean(ofplist, OFP_DEVICE_PARAM, OFP_DEVICE_ENABLE, enable);

            if(0 != mode_flag)
                g_key_file_set_integer(ofplist, OFP_DEVICE_PARAM, OFP_MODE_FLAG, 0);

            if(0 != reboot_flag)
                g_key_file_set_integer(ofplist, OFP_DEVICE_PARAM, OFP_REBOOT_FLAG, 0);

            g_key_file_save_to_file(ofplist, OFP_FPRINT_LIST_FILE, &err);
            if (err != NULL) {
                WD_ERR(_("Error[%d]: %s\n"), err->code, err->message);
                g_error_free(err);
                err = NULL;
            }
        }
    }

    ofp_conf_free(&ofplist);
    WD_DBG("end.\n");

    return 0;
}



int ofp_dev_init()
{
    dev_ofprint = ofp_dev_new();
    ofp_dev *dev = dev_ofprint;
    dev->device_name = OFPRINT_DEVICE_NAME;
    dev->full_name = OFPRINT_DEVICE_FULL_NAME;
    dev->drv_api_version.major = DRIVER_VERSION_MAJOR;//驱动软件版本
    dev->drv_api_version.minor = DRIVER_VERSION_MINOR;
    dev->drv_api_version.function = DRIVER_VERSION_FUNCTION;
    dev->enable = TRUE;
    dev->dev_num = 1;
    dev->usb_info.id_table = ofp1234_id_table;
    dev->max_user = 1;
    dev->max_sample = 50;
    dev->sample_times = 6;
    dev->ctx = NULL;

    driver_info *priv = g_new0 (driver_info, 1);
    FP_DEVICE_PARAM_STRUCT *dev_param = &(priv->fp_device_param);
    dev->dev_priv = priv;
    priv->timeoutMS = 30*1000;
    priv->dev_state = 0;
    priv->ctrlFlag = CONTROL_FLAG_IDLE;
    priv->mode_flag = 0;
    priv->reboot_flag = 0;
    memset(priv->fprint_list, 0x00, sizeof(priv->fprint_list));
    priv->set_dev_state = ofp_set_dev_state;

    dev->ops_free = ofp_ops_free;
    dev->ops_discover = ofp_ops_discover;

    ofp_set_dev_status(dev, DEVS_COMM_IDLE);
    ofp_set_ops_result(dev, OPS_COMM_SUCCESS);
    ofp_set_notify_mid(dev, NOTIFY_COMM_IDLE);

    fp_module_init(dev_param);
    upgrade_module_init();

    driver_config_init(&(priv->config));
    read_fw_upgrade_flag();
    read_fw_version();
    ofp_param_config(dev);

    return 0;
}



